/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: LianBao
 * Date: 2024-08-18
 * Time: 21:03
 */

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

/**
 * UDP 的socketAPI:
 * DatagramSocket,datagramPacket
 */

//实现一个最简单的客户端服务器程序,不涉及业务流程用于演示api做法,
//回显服务器(echo server):客户端发什么请求,服务器就返回什么响应,没有任何业务逻辑


//服务器
public class udpEchoServer {
    private DatagramSocket socket = null;

    public udpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);//对于服务器一端来说,创建socket对象需要指定端口号作为参数,后续服务器运作之后,操作系统会把端口号和进程关联起来
        //调用构造方法的过程中,JVM会调用系统的socket api,完成端口号-进程 之间的关联关系,这样的操作也叫绑定端口号
        //对于一个系统来说,同一时刻一个端口号只能被一个进程绑定,但是一个进程可以绑定多个端口号(通过创建多个socket对象来完成),端口号就是为了区分进程
    }

    //启动服务器,服务器的工作是处理客户端发来的请求

    //请求(request):客户端主动给服务器发起的数据
    //响应(response):服务器给客户端返回的数据
    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true) {
            //处理请求

            //服务器一般分为3个步骤

            //1.从网卡中读取客户端的请并且解析求
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket);
            //receive的参数是DatagramPacket对象,需要构造一个DatagramPacket对象,而构造DatagramPacket对象需要传入一个字节数组和数组长度,长度要保证能够存储数据包
            //从网卡收到的数据(是二进制数据)也会写入到数组中
            //如果网卡上收到数据,receive立即返回,并且获取到数据
            //如果网卡上没有数据,receive会阻塞等待,直到收到了数据为止
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());//因为收到的数据是二进制的,把数据转换成字符串,更好观察

            //2.根据请求计算响应,此处是回显服务器,响应就是请求
            String response = process(request);

            //3.把响应写回到客户端
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length
                    , requestPacket.getSocketAddress());
            //第一个参数是获取字节数组(因为创建String对象的时候就是使用数组的方式),
            //第二个参数是这个数组的长度,不是字符串的长度!!
            //第三个参数是客户端的信息(IP+端口号),因为UDP是无连接的,不保存双方的信息,在进行send时我们要自己把这些信息写进数据包中
            socket.send(responsePacket);

            //4.打印日志
            System.out.printf("[%s:%d] req=%s,resp=%s\n", requestPacket.getAddress(), requestPacket.getPort(),
                    request, response);
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        udpEchoServer server = new udpEchoServer(9090);
        server.start();
    }
}
